﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TsTreeTimeSeries.node
{
  
    #region Directory
    /*
     * Class thong tin mo ta
     */ 
    class Entry
    {
        #region Variable
        /**
       * Number of dimensions in a DirectoryInfo. In theory this
       * could be exended to three or more dimensions.
       */
        int DIMENSIONS = 3;
        int r_ul = 3;
        // neu la doi tuong du lieu thi cai nay ko co
        Seperator left_s;
        Seperator right_s;
        /**
        * array containing the minimum value for each dimension; ie { min(x), min(y) }
        */
        public char[] lowerBound;
        public char[] upperBound;
        // tsObject : neu o nut la va level = 1
        public tsObject realObject;

        #endregion

        /**
         * Constructor.
         * 
         */
        public Entry(char[] lowerBound, char[] upperBound)
        {


            left_s = new Seperator();
            right_s = new Seperator();

            if (lowerBound.Length != DIMENSIONS || upperBound.Length != DIMENSIONS)
            {
                throw new Exception("Error in Rectangle constructor: " +
                          "min and max arrays must be of length " + DIMENSIONS);
            }

            this.lowerBound = new char[DIMENSIONS];
            this.upperBound = new char[DIMENSIONS];

            set(lowerBound, upperBound);

            left_s = new Seperator(this.lowerBound);
            right_s = new Seperator(this.upperBound);
        }

        public Entry()
        {
            this.lowerBound = new char[DIMENSIONS];
            this.upperBound = new char[DIMENSIONS];

            left_s = new Seperator(this.lowerBound);
            right_s = new Seperator(this.upperBound);
        }
        public Entry(tsObject O)
        {
            left_s = new Seperator();
            right_s = new Seperator();

            this.lowerBound = new char[DIMENSIONS];
            this.upperBound = new char[DIMENSIONS];

            this.realObject = O;

            generate_ul();

            left_s = new Seperator(this.lowerBound);
            right_s = new Seperator(this.upperBound);
        }
        public int getdimension()
        {
            return DIMENSIONS;
        }
        /**
         * Make a copy of this DirectoryInfo
         * 
         * @return copy of this DirectoryInfo
         */
        public Entry copy()
        {
            return new Entry(this.lowerBound,this.upperBound);
        }

        public void add(Entry r)
        {
            for (int i = 0; i < DIMENSIONS; i++)
            {
                if (r.lowerBound[i] < lowerBound[i])
                {
                    lowerBound[i] = r.lowerBound[i];
                }
                if (r.upperBound[i] > upperBound[i])
                {
                    upperBound[i] = r.upperBound[i];
                }

                if (r.left_s.SeperatorInfo[i] < this.left_s.SeperatorInfo[i])
                {
                    this.left_s.SeperatorInfo[i] = r.left_s.SeperatorInfo[i];
                }
                if (r.right_s.SeperatorInfo[i] > this.right_s.SeperatorInfo[i])
                {
                    this.right_s.SeperatorInfo[i] = r.right_s.SeperatorInfo[i];
                }
            }
        }

        public void set(char[] lowerBound, char[] upperBound)
        {
            System.Array.Copy(lowerBound, 0, this.lowerBound, 0, DIMENSIONS);
            System.Array.Copy(upperBound, 0, this.upperBound, 0, DIMENSIONS);
        }

        /*
        * Tinh mindist
        * q: sau khi da ap dung PAA
        * LBUB : sau khi da ap dung PAA 
        */
        // return true neu l1 <= l2, otherwise
        internal bool Smaller(List<char> l1, List<char> l2)
        {
            // |a| ≤ |b| ∧ ∀i ∈ {1, . . . , |a|} : ai = bi 
            if (l1.Count <= l2.Count)
            {
                bool flag = true;
                for (int i = 0; i < l1.Count; i++)
                {
                    if (l1[i] != l2[i]) flag = false;
                }
                if (flag) return true;
            }
            //∃j ≤ min{|a|, |b|} ∀i ∈ {1, . . . , j − 1} : ai = bi ∧ aj < bj 
            for (int i = 0; i < Math.Min(l1.Count, l2.Count); i++)
            {
                for (int j = 0; j <= i - 1; j++)
                {
                    if (l1[j] == l2[j] && l1[i] < l2[i]) return true;
                }
            }

            return false;
        }

        // return true neu l1 >= l2, otherwise
        internal bool Bigger(List<char> l1, List<char> l2)
        {
            // |a| ≤ |b| ∧ ∀i ∈ {1, . . . , |a|} : ai = bi 
            if (l1.Count <= l2.Count)
            {
                bool flag = true;
                for (int i = 0; i < l1.Count; i++)
                {
                    if (l1[i] != l2[i]) flag = false;
                }
                if (flag) return true;
            }
            //∃j ≤ min{|a|, |b|} ∀i ∈ {1, . . . , j − 1} : ai = bi ∧ aj < bj 
            for (int i = 0; i < Math.Min(l1.Count, l2.Count); i++)
            {
                for (int j = 0; j <= i - 1; j++)
                {
                    if (l1[j] == l2[j] && l1[i] > l2[i]) return true;
                }
            }

            return false;
        }

        internal float MDMinDist(int startIndex,tsObject q)
        {
            float md = 0;
            for (int i = startIndex; i < DIMENSIONS; i++)
            {
                md +=(float) Math.Sqrt(q.tsInfo[i] > this.upperBound[i] ? Math.Pow(q.tsInfo[i] - this.upperBound[i], 2) : (q.tsInfo[i] < this.lowerBound[i] ? Math.Pow(q.tsInfo[i] - this.lowerBound[i], 2) : 0));
            }
            return md;
        }

        internal float leftMinDist(int startIndex, tsObject q)
        {
             float dist1;
             float dist2;
            if (startIndex == q.tsInfo.Count - 1)
            {
                dist1 = Math.Abs(q.tsInfo[startIndex] - this.left_s.SeperatorInfo[startIndex]);
                return dist1;
            }
            else
            {
                dist1 = Math.Abs(q.tsInfo[startIndex] - this.left_s.SeperatorInfo[startIndex]) + leftMinDist(startIndex + 1, q);
            }
            dist2 = 0;
            if (!(this.left_s.SeperatorInfo[startIndex + 1] < this.right_s.SeperatorInfo[startIndex]
                || (this.left_s.SeperatorInfo[startIndex + 1] <= this.right_s.SeperatorInfo[startIndex]
                     && this.left_s.SeperatorInfo[startIndex + 1] <= this.right_s.SeperatorInfo[startIndex + 1])
                  )
               )
            { 
                dist2 = Math.Abs(q.tsInfo[startIndex] - this.left_s.SeperatorInfo[startIndex + 1]) + MDMinDist(startIndex + 1, q); 
            }
            
            return Math.Min(dist1,dist2);
        }

        internal float rightMinDist(int startIndex, tsObject q)
        {
            float dist1;
            float dist2;

            if (startIndex == q.tsInfo.Count - 1)
            {
                dist1 = Math.Abs(q.tsInfo[startIndex] - this.right_s.SeperatorInfo[startIndex]);
                return dist1;
            }
            else
            {
                dist1 = Math.Abs(q.tsInfo[startIndex] - this.right_s.SeperatorInfo[startIndex]) + rightMinDist(startIndex + 1, q);
            }

            dist2 = 0;
            if (!(this.right_s.SeperatorInfo[startIndex + 1] < this.left_s.SeperatorInfo[startIndex]
                || (this.right_s.SeperatorInfo[startIndex + 1] <= this.left_s.SeperatorInfo[startIndex]
                     && this.right_s.SeperatorInfo[startIndex + 1] <= this.left_s.SeperatorInfo[startIndex + 1])
                  )
               )
            {
                dist2 = Math.Abs(q.tsInfo[startIndex] - this.right_s.SeperatorInfo[startIndex + 1]) + MDMinDist(startIndex + 1, q);
            }

            return Math.Min(dist1, dist2);
        }
      

        public float MinDist(Entry q)
        {
            float md = 0;
            if (q.realObject == null) 
            { 
                for (int i = 0; i < DIMENSIONS; i++)
                {
                    md += (float)Math.Sqrt(q.upperBound[i] > this.upperBound[i] ? Math.Pow(q.upperBound[i] - this.upperBound[i], 2) : (q.lowerBound[i] < this.lowerBound[i] ? Math.Pow(q.lowerBound[i] - this.lowerBound[i], 2) : 0));
                }
            }
            else
            {
                md += Smaller(q.realObject.tsInfo, left_s.SeperatorInfo) ? leftMinDist(0, q.realObject) : (Bigger(q.realObject.tsInfo, right_s.SeperatorInfo) ? rightMinDist(0, q.realObject) : 0);
            }

            return md;
        }

        public float area()
        {
            return (upperBound[0] - lowerBound[0]) * (upperBound[1] - lowerBound[1]);
        }

        public bool sameObject(object o)
        {
            return base.Equals(o);
        }

        public bool contains(Entry r)
        {
            for (int i = 0; i < DIMENSIONS; i++)
            {
                if (upperBound[i] < r.upperBound[i] || lowerBound[i] > r.lowerBound[i])
                {
                    return false;
                }
            }
            return true;
        }

        public bool edgeOverlaps(Entry r)
        {
            for (int i = 0; i < DIMENSIONS; i++)
            {
                if (lowerBound[i] == r.lowerBound[i] || upperBound[i] == r.upperBound[i])
                {
                    return true;
                }
            }
            return false;
        }

        private char MaxChar(List<char> s)
        {
            char max = s[0];
            foreach(char c in s){
                if (c > max) max = c;
            }
            return max;
        }
        private char MinChar(List<char> s)
        {
            char min = s[0];
            foreach (char c in s)
            {
                if (c < min) min = c;
            }
            return min;
        }

        public void generate_ul()
        {
            if (this.realObject != null)
            {
                for (int i = 0; i < this.realObject.tsInfo.Count; i++ )
                {
                    int startIndex = i-r_ul<0?0:i-r_ul;
                    int length = startIndex + r_ul * 2 < this.realObject.tsInfo.Count ? r_ul * 2 : this.realObject.tsInfo.Count - startIndex;
                    this.upperBound[i] = MaxChar(this.realObject.tsInfo.GetRange(startIndex,length));
                    this.lowerBound[i] = MinChar(this.realObject.tsInfo.GetRange(startIndex, length));
                }
            }
        }
        public string EntryToString()
        {
            string str = "";
      
            str += "{";
            // add left sepeartor
            //str += "[";
            //foreach(char c in this.left_s.SeperatorInfo)
            //{
            //    str += c + ",";
            //}
            //str += "]";

            // ADD INFO
            str += "<";
            if (this.realObject != null)
            {
                foreach (char c in this.realObject.tsInfo)
                {
                    str += c + ",";
                }
            }
            else
            {
                foreach (char c in this.lowerBound)
                {
                    str += c + ",";
                }
                str += "/";
                foreach (char c in this.upperBound)
                {
                    str += c + ",";
                }

            }
            str += ">";
            // add right sepeartor
            //str += "[";
            //foreach (char c in this.right_s.SeperatorInfo)
            //{
            //    str += c + ",";
            //}
            //str += "]";
            str += "}";


            return str;

        }
    }

    #endregion


}
